home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume1 / vnews / part7 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  57.4 KB

  1. From: decvax!vax135!hou3c!ka
  2. Date: Mon, 21 Jan 85 22:39:59 est
  3. Newsgroups: mod.sources
  4. Subject: Vnews part 7
  5.  
  6. # Welcome to vnews release 2.11-B 1/17/85.
  7. # This is part 7 out of 7.
  8. # Feed me into sh (NOT csh).
  9.  
  10. if test ! -d vnews
  11. then    mkdir vnews
  12. fi
  13.  
  14. cat > vnews/virtterm.c <<\!E!O!F!
  15. /*
  16.  *  Virtual terminal handler
  17.  *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
  18.  *  Modified by Stephen Hemminger, to use TERMCAP (without curses)
  19.  */
  20.  
  21. #include <stdio.h>
  22. #include <ctype.h>
  23.  
  24. #define ANSI    /* support multiple line insert/delete */
  25. #define MAXPLEN 24
  26. #define MAXLLEN 80
  27. #define BOTLINE (ROWS - 1)
  28. #define DIRTY 01
  29.  
  30. /* terminal escape sequences from termcap */
  31. #define HO _tstr[0]        /* home */
  32. #define CL _tstr[1]        /* clear screen */
  33. #define CD _tstr[2]        /* clear to end of screen */
  34. #define CE _tstr[3]        /* clear to end of line */
  35. #define xUP _tstr[4]        /* up one line */
  36. #define DO _tstr[5]        /* down one line */
  37. #define US _tstr[6]        /* underline */
  38. #define UE _tstr[7]        /* underline end */
  39. #define BT _tstr[8]        /* backtab */
  40. #define xBC _tstr[9]        /* backspace */
  41. #define AL _tstr[10]        /* insert line */
  42. #define DL _tstr[11]        /* delete line */
  43. #define CM _tstr[12]        /* cursor move */
  44. #define CH _tstr[13]        /* cursor horizontal move */
  45. #define CV _tstr[14]        /* cursor vertical move */
  46. #define CS _tstr[15]        /* scrolling region */
  47. #define SF _tstr[16]        /* scroll forwards */
  48. #define SR _tstr[17]        /* scroll backwards */
  49. #define TI _tstr[18]        /* start cursor mode */
  50. #define TE _tstr[19]        /* end cursor mode */
  51. #define TA _tstr[20]        /* tab char (if not \t) */
  52. #define CR _tstr[21]        /* carriage return (if not \r) */
  53. #define xPC _tstr[22]        /* for reading pad character */
  54. #ifdef ANSI
  55. #define ALC _tstr[23]        /* insert multiple lines */
  56. #define DLC _tstr[24]        /* delete multiple lines */
  57. #endif
  58. char PC;            /* pad character */
  59. char *BC, *UP;            /* external variables for tgoto */
  60.  
  61. #ifdef ANSI
  62. static char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpcALDL";
  63. char *_tstr[25];
  64. #else
  65. static char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc";
  66. char *_tstr[23];
  67. #endif
  68. int     HOlen;            /* length of HO string */
  69.  
  70.  
  71. /* terminal flags */
  72. #define BS _tflg[0]        /* can backspace */
  73. #define AM _tflg[1]        /* has auto margins */
  74. #define XN _tflg[2]        /* no newline after wrap */
  75. #define RET !_tflg[3]        /* has carriage return */
  76. #define NS _tflg[4]        /* has SF (scroll forward) */
  77. #define PT _tflg[5]        /* has tabs */
  78. #define XT _tflg[6]        /* tabs are destructive */
  79. int    GT = 1;            /* tab stops on terminal are set */
  80.  
  81. static char bname[] = "bsamxnncnsptxt";
  82. char _tflg[7];
  83.  
  84.  
  85. extern char *tgoto(), *tgetstr();
  86. extern char *getenv(), *strcpy();
  87.  
  88. #define ULINE 0200
  89. #define CURSEEN 1
  90.  
  91. /* Constants accessable by user */
  92. int     hasscroll;        /* scrolling type, 0 == no scrolling */
  93. int     ROWS;            /* number of lines on screen */
  94. int     COLS;            /* width of screen */
  95. int    needbeep;        /* user sets this to generate beep */
  96.  
  97. struct line {
  98.     char    len;
  99.     char    flags;
  100.     char    l[MAXLLEN];
  101. };
  102.  
  103. int     _row, _col;
  104. int     _srow, _scol;
  105. struct line _virt[MAXPLEN], _actual[MAXPLEN];
  106. int     _uline = 0;
  107. int     _junked = 1;
  108. int     _curjunked;
  109. int    _partupd;
  110. int     _dir = 1;
  111. int    _shifttop, _shiftbot;
  112. int    _shift;
  113. int    _scratched;
  114. int     vputc();
  115.  
  116.  
  117.  
  118. /*
  119.  * Tell refresh to shift lines in region upwards count lines.  Count
  120.  * may be negative.  The virtual image is not shifted; this may change
  121.  * later.  The variable _scratched is set to supress all attempts to
  122.  * shift.
  123.  */
  124.  
  125. ushift(top, bot, count) {
  126.     if (_scratched)
  127.         return;
  128.     if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) {
  129.         _scratched++;
  130.         return;
  131.     }
  132.     _shifttop = top;
  133.     _shiftbot = bot;
  134.     _shift += count;
  135. }
  136.  
  137.  
  138.  
  139. /*
  140.  * generate a beep on the terminal
  141.  */
  142.  
  143. beep() {
  144.     vputc('\7');
  145. }
  146.  
  147. /*
  148.  * Move to one line below the bottom of the screen.
  149.  */
  150.  
  151. botscreen() {
  152.     _amove(BOTLINE, 0);
  153.     vputc('\n');
  154.     vflush();
  155. }
  156.  
  157.  
  158.  
  159. move(row, col) {
  160.     if (row < 0 || row >= ROWS || col < 0 || col >= COLS)
  161.         return;
  162.     _row = row;
  163.     _col = col;
  164. }
  165.  
  166.  
  167.  
  168. /*
  169.  * Output string at specified location.
  170.  */
  171.  
  172. mvaddstr(row, col, str)
  173.     char *str;
  174.     {
  175.     move(row, col);
  176.     addstr(str);
  177. }
  178.  
  179.  
  180. addstr(s)
  181. char   *s;
  182. {
  183.     register char  *p;
  184.     register struct line   *lp;
  185.     register int    col = _col;
  186.  
  187.     lp = &_virt[_row];
  188.     if (lp->len < col) {
  189.         p = &lp->l[lp->len];
  190.         while (lp->len < col) {
  191.             *p++ = ' ';
  192.             lp->len++;
  193.         }
  194.     }
  195.     for (p = s; *p != '\0'; p++) {
  196.         if (*p == '\n') {
  197.             lp->len = col;
  198.             lp->flags |= DIRTY;
  199.             col = 0;
  200.             if (++_row >= ROWS)
  201.                 _row = 0;
  202.             lp = &_virt[_row];
  203.         }
  204.         else {
  205.             lp->l[col] = *p;
  206.             lp->flags |= DIRTY;
  207.             if (++col >= COLS) {
  208.                 lp->len = COLS;
  209.                 col = 0;
  210.                 if (++_row >= ROWS)
  211.                     _row = 0;
  212.                 lp = &_virt[_row];
  213.             }
  214.         }
  215.     }
  216.     if (lp->len <= col)
  217.         lp->len = col;
  218.     _col = col;
  219. }
  220.  
  221.  
  222.  
  223. addch (c) {
  224.     register struct line   *lp;
  225.     register char  *p;
  226.  
  227.     lp = &_virt[_row];
  228.     if (lp->len < _col) {
  229.         p = &lp->l[lp->len];
  230.         while (lp->len < _col) {
  231.             *p++ = ' ';
  232.             lp->len++;
  233.         }
  234.     }
  235.     lp->l[_col] = c;
  236.     if (lp->len == _col)
  237.         lp->len++;
  238.     if (++_col >= COLS) {
  239.         _col = 0;
  240.         if (++_row >= ROWS)
  241.             _row = 0;
  242.     }
  243.     lp->flags |= DIRTY;
  244. }
  245.  
  246.  
  247.  
  248. clrtoeol() {
  249.     register struct line   *lp;
  250.  
  251.     lp = &_virt[_row];
  252.     if (lp->len > _col) {
  253.         lp->len = _col;
  254.         lp->flags |= DIRTY;
  255.     }
  256. }
  257.  
  258.  
  259. /*
  260.  * Clear an entire line.
  261.  */
  262.  
  263. clrline(row) {
  264.     register struct line   *lp;
  265.  
  266.     lp = &_virt[row];
  267.     if (lp->len > 0) {
  268.         lp->len = 0;
  269.         lp->flags |= DIRTY;
  270.     }
  271. }
  272.  
  273.  
  274.  
  275. clear() {
  276.     erase();
  277.     _junked++;
  278. }
  279.  
  280.  
  281.  
  282. erase() {
  283.     register    i;
  284.  
  285.     for (i = 0; i < ROWS; i++) {
  286.         _virt[i].len = 0;
  287.         _virt[i].flags |= DIRTY;
  288.     }
  289. }
  290.  
  291.  
  292.  
  293. /*
  294.  * Leave lines as they are on the screen.
  295.  */
  296.  
  297. nochange(line1, line2) {
  298.     while (line1 <= line2) {
  299.         _virt[line1] = _actual[line1] ;
  300.         if (_junked)
  301.             clrline(line1) ;
  302.         line1++ ;
  303.     }
  304. }
  305.  
  306.  
  307.  
  308. refresh() {
  309.     register int i;
  310.     register int j;
  311.     int len;
  312.     int needchk;
  313.     register char *p, *q;
  314.  
  315. /*
  316.     if (checkin())
  317.         return;
  318. */
  319.     needchk = 0;
  320.     i = 1;
  321.     if (_junked) {
  322.         _sclear();
  323.         _junked = 0;
  324.     } else if (! _scratched) {
  325.         if (_shift) {
  326.             needchk++;
  327.             if (_shift > 0)
  328.             _ushift(_shifttop, _shiftbot, _shift);
  329.             else
  330.             i = _dshift(_shifttop, _shiftbot, -_shift);
  331.         } else {
  332.             i = _dir;
  333.         }
  334.     }
  335.     _dir = i;
  336.     _dir = 1;        /* forget about _dir stuff */
  337.     _shift = 0;
  338.     _partupd = 1;
  339.     if (needchk && checkin())
  340.         return;
  341.     _scratched = 0;
  342.     _fixlines();
  343.     for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) {
  344.         if ((_virt[i].flags & DIRTY) == 0)
  345.             continue;
  346.         _ckclrlin(i);        /* decide whether to do a clear line */
  347.                     /* probably should consider cd as well */
  348.         len = _virt[i].len;
  349.         if (_actual[i].len < len)
  350.             len = _actual[i].len;
  351.         p = _virt[i].l;
  352.         q = _actual[i].l;
  353.         for (j = 0; j < len; j++) {
  354.             if (*p != *q) {
  355.                 _amove(i, j);
  356.                 _aputc(*p);
  357.                 *q = *p;
  358.             }
  359.             p++, q++;
  360.         }
  361.         len = _virt[i].len;
  362.         if (_actual[i].len > len) {
  363.             _clrtoeol(i, len);
  364.         }
  365.         else {
  366.             for (; j < len; j++) {
  367.                 if (*p != ' ') {
  368.                     _amove(i, j);
  369.                     _aputc(*p);
  370.                 }
  371.                 *q++ = *p++;
  372.             }
  373.             _actual[i].len = len;
  374.         }
  375.         if (checkin())
  376.             return;
  377.     }
  378.     _dir = 1;
  379.     if (needbeep) {
  380.         beep();
  381.         needbeep = 0;
  382.     }
  383.     if (CURSEEN)
  384.         _amove(_row, _col);
  385.     vflush();            /* flush output buffer */
  386.     _partupd = 0;
  387. }
  388.  
  389.  
  390. #define badshift(i, count) (_actual[i].len != _virt[i+count].len || strncmp(_actual[i].l, _virt[i+count].l, _actual[i].len) != 0)
  391.  
  392. _dshift(top, bot, count) {
  393.     register    i;
  394.  
  395.     if (count >= bot - top || hasscroll < 4) {  /* must have CS or AL/DL */
  396.         _scratched++;
  397.         return 1;
  398.     }
  399.     for (i = bot - count; _actual[i].len == 0 || _partupd && badshift(i, count); i--)
  400.         if (i == top)
  401.             return 1;
  402.     for (i = top; i <= bot; i++)
  403.         _virt[i].flags |= DIRTY;
  404.     for (i = bot; i >= top + count; i--)
  405.         _actual[i] = _actual[i - count];
  406.     for (; i >= top; i--)
  407.         _actual[i].len = 0;
  408.  
  409.     if (hasscroll != 5) {        /* can we define scrolling region, and scroll back */
  410.         tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */
  411.         _curjunked = 1;
  412.         _amove(top, 0);
  413.         for (i = count; --i >= 0;)
  414.             tputs(SR, 1, vputc);/* scroll back */
  415.         tputs(tgoto(CS, BOTLINE, 0), 1, vputc);
  416.         _curjunked = 1;
  417.     } else {
  418.         _amove(bot - count + 1, 0);
  419.         if (CD && bot == BOTLINE)
  420.             tputs(CD, 1, vputc);
  421.         else {
  422. #ifdef ANSI
  423.             if (DLC && count > 1)
  424.                 tputs(tgoto(DLC, count, count), 0, vputc);
  425.             else
  426. #endif
  427.             for (i = count; --i >= 0;)
  428.                 tputs(DL, ROWS - _srow, vputc);
  429.         }
  430.         _amove(top, 0);
  431. #ifdef ANSI
  432.         if (count > 1 && ALC)
  433.             tputs(tgoto(ALC, count, count), 0, vputc), count=0;
  434. #endif
  435.         for (i = count; --i >= 0;)
  436.             tputs(AL, ROWS - _srow, vputc);
  437.     }
  438.     return -1;
  439. }
  440.  
  441.  
  442. _ushift(top, bot, count) {
  443.     register    i;
  444.  
  445.     if (count >= bot - top || hasscroll == 0) {
  446.         return;
  447.     }
  448.     for (i = top + count; _actual[i].len == 0 || _partupd && badshift(i, -count); i++)
  449.         if (i == bot)
  450.             return;
  451.     if (hasscroll == 1 || hasscroll == 3) {
  452.         /* we cheat and shift the entire screen */
  453.         /* be sure we are shifting more lines into than out of position */
  454.         if ((bot - top + 1) - count <= ROWS - (bot - top + 1))
  455.             return;
  456.         top = 0, bot = BOTLINE;
  457.     }
  458.     for (i = top; i <= bot; i++)
  459.         _virt[i].flags |= DIRTY;
  460.     for (i = top; i <= bot - count; i++)
  461.         _actual[i] = _actual[i + count];
  462.     for (; i <= bot; i++)
  463.         _actual[i].len = 0;
  464.  
  465.     if (hasscroll != 5) {
  466.         if (top != 0 || bot != BOTLINE) {
  467.             tputs(tgoto(CS, bot, top), 0, vputc);
  468.             _curjunked = 1;
  469.         }
  470.         _amove(bot, 0);    /* move to bottom */
  471.         for (i = 0; i < count; i++) {
  472.             if (SF)        /* scroll forward */
  473.                 tputs(SF, 1, vputc);
  474.             else
  475.                 vputc('\n');
  476.         }
  477.         if (top != 0 || bot != BOTLINE) {
  478.             tputs(tgoto(CS, BOTLINE, 0), 0, vputc);
  479.             _curjunked = 1;
  480.         }
  481.     } else {
  482.         _amove(top, 0);
  483. #ifdef ANSI
  484.         if (DLC && count > 1)
  485.             tputs(tgoto(DLC, count, count), 0, vputc);
  486.         else
  487. #endif
  488.         for (i = count; --i >= 0;)
  489.             tputs(DL, ROWS - _srow, vputc);
  490.         if (bot < BOTLINE) {
  491.             _amove(bot - count + 1, 0);
  492. #ifdef ANSI
  493.             if (ALC && count > 1)
  494.                 tputs(tgoto(ALC, count, count), 0, vputc), count=0;
  495. #endif
  496.             for (i = count; --i >= 0;)
  497.                 tputs(AL, ROWS - _srow, vputc);
  498.         }
  499.     }
  500. }
  501.  
  502.  
  503. _sclear() {
  504.     register struct line   *lp;
  505.  
  506.     tputs(CL, 0, vputc);
  507.     _srow = _scol = 0;
  508.     for (lp = _actual; lp < &_actual[ROWS]; lp++) {
  509.         lp->len = 0;
  510.     }
  511.     for (lp = _virt; lp < &_virt[ROWS]; lp++) {
  512.         if (lp->len != 0)
  513.             lp->flags |= DIRTY;
  514.     }
  515. }
  516.  
  517.  
  518. _clrtoeol(row, col) {
  519.     register struct line *lp = &_actual[row];
  520.     register i;
  521.  
  522.     if (CE && lp->len > col + 1) {
  523.         _amove(row, col);
  524.         tputs(CE, 1, vputc);
  525.     } else {
  526.         for (i = col ; i < lp->len ; i++) {
  527.             if (lp->l[i] != ' ') {
  528.                 _amove(row, i);
  529.                 _aputc(' ');
  530.             }
  531.         }
  532.     }
  533.     lp->len = col;
  534. }
  535.  
  536.  
  537. _fixlines() {
  538.     register struct line   *lp;
  539.     register char  *p;
  540.     register int    i;
  541.  
  542.     for (i = 0; i < ROWS; i++) {
  543.         lp = &_virt[i];
  544.         if (lp->flags & DIRTY) {
  545.             lp = &_virt[i];
  546.             for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';);
  547.             lp->len = (int)(p - lp->l) + 1;
  548.             if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
  549.                 lp->flags &= ~DIRTY;
  550.         }
  551.     }
  552. }
  553.  
  554.  
  555. /*
  556.  * Consider clearing the line before overwriting it.
  557.  * We always clear a line if it has underlined characters in it
  558.  * because these can cause problems.  Otherwise decide whether
  559.  * that will decrease the number of characters to change.  This
  560.  * routine could probably be simplified with no great loss.
  561.  */
  562.  
  563. _ckclrlin(i) {
  564.     int     eval;
  565.     int     len;
  566.     int     first;
  567.     register struct line   *vp, *ap;
  568.     register int    j;
  569.  
  570.     if (!CE)
  571.         return;
  572.     ap = &_actual[i];
  573.     vp = &_virt[i];
  574.     len = ap->len;
  575.     eval = -strlen(CE);
  576.     if (len > vp->len) {
  577.         len = vp->len;
  578.         eval = 0;
  579.     }
  580.     for (j = 0; j < len && vp->l[j] == ap->l[j]; j++);
  581.     if (j == len)
  582.         return;
  583.     first = j;
  584.     while (j < len) {
  585.         if (vp->l[j] == ' ') {
  586.             if (ap->l[j] != ' ') {
  587.                 while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') {
  588.                     eval++;
  589.                 }
  590.                 if (j == len)
  591.                     eval++;
  592.                 continue;
  593.             }
  594.         }
  595.         else {
  596.             if (vp->l[j] == ap->l[j]) {
  597.                 while (++j < len && vp->l[j] == ap->l[j]) {
  598.                     eval--;
  599.                 }
  600.                 continue;
  601.             }
  602.         }
  603.         j++;
  604.     }
  605.     if (US) {
  606.         for (j = 0 ; j < ap->len ; j++) {
  607.             if (ap->l[j] & ULINE) {
  608.                 eval = 999;
  609.                 if (first > j)
  610.                     first = j;
  611.                 break;
  612.             }
  613.         }
  614.     }
  615.     for (j = first; --j >= 0;)
  616.         if (vp->l[j] != ' ')
  617.             break;
  618.     if (j < 0)
  619.         first = 0;
  620.     if (eval > 0) {
  621.         _amove(i, first);
  622.         tputs(CE, 0, vputc);
  623.         _actual[i].len = first;
  624.     }
  625. }
  626.  
  627.  
  628.  
  629. /*
  630.  * Move routine
  631.  *     first compute direct cursor address string and cost
  632.  *    then relative motion string and cost,
  633.  *    then home then relative and cost
  634.  *    choose smallest and do it.
  635.  *
  636.  *    The plod stuff is to build the strings (with padding) then decide
  637.  */
  638. static char *plodstr;        /* current location in relmove string */
  639.  
  640. plodput(c) { *plodstr++ = c; }
  641.  
  642. _amove(row, col) {
  643.     char direct[20];
  644.     char rel[4 * MAXPLEN + 3 * MAXLLEN];
  645.     char ho[3 * MAXPLEN + 3 * MAXLLEN];
  646.     int cost, newcost;
  647.     register char *movstr;
  648.  
  649.     if (row == _srow && col == _scol && _curjunked == 0)
  650.         return;
  651.     _setul(0);
  652.  
  653.     cost = 999;
  654.     if (CM) {
  655.         plodstr = direct;
  656.         tputs(tgoto(CM, col, row), 0, plodput);
  657.         *plodstr = '\0';
  658.         cost = plodstr - direct;
  659.         movstr = direct;
  660.     }
  661.     if (_curjunked == 0) {
  662.         plodstr = rel;
  663.         if (_vmove(_srow, row) >= 0
  664.          && plodstr - rel < cost
  665.          && _hmove(_scol, col, row) >= 0
  666.          && (newcost = plodstr - rel) < cost) {
  667.             *plodstr = '\0';
  668.             cost = newcost;
  669.             movstr = rel;
  670.         }
  671.     }
  672.     if (cost > HOlen) {    /* is it worth calculating */
  673.         plodstr = ho;
  674.         tputs(HO, 0, plodput);
  675.         if (_vmove(0, row) >= 0
  676.          && plodstr - rel < cost
  677.          && _hmove(0, col, row) >= 0
  678.          && (newcost = plodstr - ho) < cost) {
  679.             *plodstr = '\0';
  680.             cost = newcost;
  681.             movstr = ho;
  682.         }
  683.     }
  684.  
  685.     while (--cost >= 0) {
  686.         vputc(*movstr++);
  687.     }
  688.     _srow = row, _scol = col;
  689.     _curjunked = 0;
  690. }
  691.  
  692.  
  693. _vmove(orow, nrow) {
  694.     char direct[64];
  695.     char *saveplod = plodstr;
  696.  
  697.     if (CV) {
  698.         plodstr = direct;
  699.         tputs(tgoto(CV, nrow, nrow), 0, plodput);
  700.         *plodstr = '\0';
  701.         plodstr = saveplod;
  702.     }
  703.     if (orow > nrow) {        /* cursor up */
  704.         if (! UP)
  705.             return -1;
  706.         while (orow > nrow) {
  707.             tputs(UP, 1, plodput);
  708.             orow--;
  709.         }
  710.     }
  711.     while (orow < nrow) {        /* cursor down */
  712.         if (DO)
  713.             tputs(DO, 1, plodput);
  714.         else
  715.             *plodstr++ = '\n';
  716.         orow++;
  717.     }
  718.     if (CV && plodstr - saveplod >= strlen(direct)) {
  719.         register char *p;
  720.         plodstr = saveplod;
  721.         for (p = direct ; *plodstr = *p++ ; plodstr++);
  722.     }
  723.     return 0;
  724. }
  725.  
  726.  
  727. _hmove(ocol, ncol, row) {
  728.     char direct[64];
  729.     char ret[3 * MAXLLEN];
  730.     char *saveplod = plodstr;
  731.     char *movstr;
  732.     int cost, newcost;
  733.  
  734.     cost = 999;
  735.     if (CH) {
  736.         plodstr = direct;
  737.         tputs(tgoto(CH, ncol, ncol), 0, plodput);
  738.         cost = plodstr - direct;
  739.         movstr = direct;
  740.         plodstr = saveplod;
  741.     }
  742.     if (RET && ocol > ncol) {    /* consider doing carriage return */
  743.         plodstr = ret;
  744.         if (CR)
  745.             tputs(CR, 1, plodput);
  746.         else
  747.             *plodstr++ = '\r';
  748.         if (_relhmove(0, ncol, row) >= 0
  749.          && (newcost = plodstr - ret) < cost) {
  750.             cost = newcost;
  751.             movstr = ret;
  752.         }
  753.         plodstr = saveplod;
  754.     }
  755.     if (_relhmove(ocol, ncol, row) < 0) {
  756.         if (cost == 999)
  757.             return -1;
  758.         goto copy;
  759.     }
  760.     if (plodstr - saveplod > cost) {
  761. copy:        plodstr = saveplod;
  762.         while (--cost >= 0)
  763.             *plodstr++ = *movstr++;
  764.     }
  765.     return 0;
  766. }
  767.  
  768.  
  769.  
  770. _relhmove(ocol, ncol, row) {
  771.     int tab;
  772.  
  773.     if (ocol < ncol && PT && GT) {    /* tab (nondestructive) */
  774.         while ((tab = (ocol + 8) & ~07) <= ncol) {
  775.             if (TA)
  776.                 tputs(TA, 1, plodput);
  777.             else
  778.                 *plodstr++ = '\t';
  779.             ocol = tab;
  780.         }
  781.         if (tab < COLS && tab - ncol < ncol - ocol) {
  782.             if (TA)
  783.                 tputs(TA, 1, plodput);
  784.             else
  785.                 *plodstr++ = '\t';
  786.             ocol = tab;
  787.         }
  788.     } else if (BT && GT && ocol > ncol) {    /* backwards tab */
  789.         while ((tab = (ocol - 1) &~ 07) >= ncol) {
  790.             if (BS && tab == ocol - 1) {
  791.                 if (BC)
  792.                     tputs(BC, 1, plodput);
  793.                 else
  794.                     *plodstr++ = '\b';
  795.             } else
  796.                 tputs(BT, 1, plodput);
  797.             ocol = tab;
  798.         }
  799.         if (ncol - tab + 1 < ocol - ncol) {
  800.             tputs(BT, 1, plodput);
  801.             ocol = tab;
  802.         }
  803.     }
  804.     if (ocol > ncol) {            /* cursor left */
  805.         if (! BS)
  806.             return -1;
  807.         while (ocol > ncol) {
  808.             if (BC != NULL)
  809.                 tputs(BC, 1, plodput);
  810.             else
  811.                 *plodstr++ = '\b';
  812.             ocol--;
  813.         }
  814.     }
  815.     if (ocol < ncol) {            /* cursor right */
  816.         register struct line *lp = &_actual[row];
  817.         /*
  818.          * This code doesn't move over underlined characters properly,
  819.          * but in practice this doesn't seem to matter.
  820.          */
  821.         while (ocol < ncol) {
  822.             if (ocol < lp->len)
  823.                 *plodstr++ = lp->l[ocol];
  824.             else
  825.                 *plodstr++ = ' ';
  826.             ocol++;
  827.         }
  828.     }
  829.     return 0;
  830. }
  831.  
  832.  
  833.  
  834. _aputc(c) {
  835.     if (AM && _scol == COLS - 1 && _srow == ROWS - 1)
  836.         return;
  837.     _setul(c & ULINE);
  838.     vputc(c & ~ULINE);
  839.     if (++_scol >= COLS) {
  840.         if (! AM) {
  841.             _scol--;
  842.         } else  if (XN) {
  843.             _curjunked++;
  844.         } else {
  845.             _scol = 0;
  846.             ++_srow;
  847.         }
  848.     }
  849. }
  850.  
  851.  
  852. _setul(on) {
  853.     if (on) {
  854.         if (_uline == 0 && US != NULL) {
  855.             tputs(US, 1, vputc);
  856.             _uline = 1;
  857.         }
  858.     }
  859.     else {
  860.         if (_uline != 0 && UE != NULL) {
  861.             tputs(UE, 1, vputc);
  862.             _uline = 0;
  863.         }
  864.     }
  865. }
  866.  
  867.  
  868. /*
  869.  * Initialize termcap strings for later use.
  870.  */
  871. initterm() {
  872.     static char tcbuf[1024];    /* termcap buffer */
  873.     register char  *cp;
  874.  
  875.     if ((cp = getenv("TERM")) == NULL)
  876.         xerror("TERM not set in environment");
  877.  
  878.     switch (tgetent(tcbuf, cp)) {
  879.         case 0:
  880.             xerror("Terminal not found in TERMCAP");
  881.         case -1:
  882.             xerror("Can't open /etc/termcap");
  883.         case 1:
  884.             break;
  885.     }
  886.  
  887.     if ((ROWS = tgetnum("li")) == -1
  888.      || (COLS = tgetnum("co")) == -1)
  889.         xerror("Can't get screen size");
  890.     _zap();
  891.  
  892.     if (CL == NULL)
  893.         xerror ("No clear screen defined");
  894.  
  895.     if (HO == NULL && CM == NULL)
  896.         xerror("No home or cursor addressing");
  897.     if (HO)
  898.         HOlen = strlen(HO);
  899.     else
  900.         HOlen = 999;
  901.  
  902.     PC = xPC ? xPC[0] : 0;
  903.     BC = xBC;
  904.     UP = xUP;
  905.  
  906.     if (tgetnum("ug") > 0)
  907.         US = UE = NULL;
  908.  
  909.     if (XT)                /* Destructive tab code not included */
  910.         PT = 0;            /* to keep things simple */
  911.  
  912.     if (ROWS > MAXPLEN)
  913.         ROWS = MAXPLEN;
  914.     if (COLS > MAXLLEN) {
  915.         COLS = MAXLLEN;
  916.         AM = XN = 1;
  917.     }
  918.  
  919.     /* Select article scrolling algorithm.  We prefer scrolling region
  920.        over insert/delete line because it's faster on the HP */
  921.     hasscroll = 0;
  922.     if (!NS) {
  923.         hasscroll = 1;
  924.         if (SR)
  925.             hasscroll = 3;
  926.         if (CS)
  927.             hasscroll++;
  928.     }
  929.     if (AL && DL && hasscroll != 4)
  930.         hasscroll = 5;
  931. }
  932.  
  933.  
  934. rawterm() {
  935.     if (TI != NULL)
  936.         tputs(TI, 0, vputc);
  937. }
  938.  
  939.  
  940. cookedterm() {
  941.     if (TE != NULL) {
  942.         tputs(TE, 0, vputc);
  943.         vflush() ;
  944.     }
  945. }
  946.  
  947.  
  948. /* get strings from termcap */
  949. _zap() {
  950.     static char tstrbuf[1024];
  951.     static char *tp;
  952.     register char  *namp, **sp, *bp;
  953.  
  954.     tp = tstrbuf;
  955.     sp = _tstr;
  956.     for (namp = sname; *namp; namp += 2) {
  957.         *sp++ = tgetstr(namp, &tp);
  958.     }
  959.     bp = _tflg;
  960.     for (namp = bname; *namp; namp += 2) {
  961.         *bp++ = tgetflag(namp, &tp);
  962.     }
  963. }
  964. !E!O!F!
  965.  
  966. cat > vnews/virtterm.doc <<\!E!O!F!
  967. .. Run this file through nroff (no macro packages required).
  968. .ll 72
  969. .na
  970. .hy
  971. The new virtterm.c uses termcap.  This makes it larger, but not excessively
  972. so.
  973. The cursor motion commands are longer for the HP and VT100 terminals
  974. due to the inability of termcap to completely describe these terminals,
  975. but this doesn't make a big difference.
  976. Virtterm handles both insert/delete line and VT100 style scrolling regions.
  977. It also handles terminals with sticky underlining (like some HP terminals).
  978.  
  979. The following is the list of the terminal capabilities virtterm uses:
  980. .nf
  981.  
  982.     bc, bs        Cursor left
  983.     do        Cursor down
  984.     up        Cursor up
  985.     ta, pt        Nondestructive tab
  986.     bt        Backwards tab
  987.     ho        Cursor home
  988.     cr, nc        Carriage return
  989.     cm        Cursor to specified location
  990.     ch, cv        Cursor to specified column or row
  991.     am, xn        Terminal wrap-around
  992.     al, dl        Insert and delete line
  993.     AL, DL        Insert and delete multiple lines
  994.     cs, sr        Scrolling region (a al VT100)
  995.     sf, ns        Scroll entire screen
  996.     ce        Clear to end of line
  997.     cl        Clear screen and move cursor home
  998.     us, ue        Underscore mode
  999.     ti, te        Turn visual mode on/off
  1000.  
  1001. .fi
  1002. In order to run with virtterm, the terminal *must* have:
  1003. .in 11
  1004. .ti -3
  1005. 1)\ Clear screen.
  1006. .ti -3
  1007. 2)\ Non-relative cursor motion (either cm or ho).
  1008. .ti -3
  1009. 3)\ Cursor down (linefeed will be used if do is not specified).
  1010. .ti -3
  1011. 4)\ Us and ue, otherwise no underlining will be done.
  1012. .ti -3
  1013. 5)\ Tabstops set every 8 columns if terminal has tabs.
  1014. Virtterm doesn't bother to set the tabs; it assumes they are already set.
  1015. .in 0
  1016.  
  1017. The new virtterm can't do its job very well if capabilities are missing
  1018. from the termcap entry.
  1019. For example, in our version of /etc/termcap, us and ue entries
  1020. were not specified the HP-2621.
  1021. !E!O!F!
  1022.  
  1023. cat > vnews/vnews.1 <<\!E!O!F!
  1024. .TH VNEWS 1
  1025. .SH NAME
  1026. vnews \- read news articles
  1027. .SH SYNOPSIS
  1028. .BR vnews " [ " \-a
  1029. .IR date " ] [ "
  1030. .B \-n
  1031. .IR newsgroups " ] [ "
  1032. .B \-t
  1033. .IR titles " ] [ "
  1034. .BR \-rxhfuA " ] [ "
  1035. .BR \-c " ]"
  1036. .PP
  1037. .B "vnews \-s"
  1038. .SH DESCRIPTION
  1039. .SS "Overview"
  1040. .. .RS 5
  1041. .B Vnews
  1042. is a program for reading USENET news.
  1043. It is based on
  1044. .BR readnews (1)
  1045. but has a CRT oriented user interface.
  1046. The command line options are identical,
  1047. but only the options listed in the synopsis are supported.
  1048. The effect of the
  1049. .B -c
  1050. option is different in vnews than in readnews; it
  1051. causes vnews to display the entire first page of an article
  1052. immediately rather than initially showing only the header.
  1053. The
  1054. .B -A
  1055. option causes the newsgroup index to be displayed each time
  1056. a new group is entered.
  1057. The list of available commands is quite similar, although since
  1058. .B vnews
  1059. is a "visual" interface, most
  1060. .B vnews
  1061. commands do not have to be terminated by a newline.
  1062. .B Vnews
  1063. is somewhat less friendly than
  1064. .BR readnews ,
  1065. so new users should probably start out with readnews.
  1066. .. .P
  1067. .. .B Vnews
  1068. .. is an experiment in writing a good user interface.
  1069. .. The only way I can tell how successful I have been is by the responses I
  1070. .. receive, so please send you comments, good or bad, to
  1071. .. {harpo|ihnp4|burl|ucbvax!mhtsa}!spanky!ka.
  1072. .. (Bug reports should be sent to the same address.)
  1073. .P
  1074. .B Vnews
  1075. uses
  1076. all except the last two
  1077. .. all except the first two
  1078. lines of the screen to display the current article.
  1079. The second to the bottom line
  1080. .. Line 2
  1081. is the secondary prompt line, and is used to input string arguments
  1082. to commands.
  1083. The bottom line
  1084. .. Line 1
  1085. contains several fields.
  1086. .. The first field is the prompt field.
  1087. .. If
  1088. .. .B vnews
  1089. .. is at the end of an article, the prompt is "next?"; otherwise the prompt is
  1090. .. "more?".
  1091. The first field contains an approximation of the percentage of the
  1092. article that has been read.  It will be 100% if and only if the entire
  1093. article has been read.
  1094. .. [This is a more informative (and cryptic) replacement for the
  1095. .. more/next indication.]  \
  1096. ..
  1097. The second field is the newsgroup field, which displays the current
  1098. newsgroup, the number of the current article, and the number of the last
  1099. article in the newsgroup.
  1100. The third field contains the current time, and the last field contains the
  1101. word "mail" if you have mail.
  1102. When you receive new mail, the bell on the terminal is rung and the
  1103. word "MAIL"
  1104. appears in capital letters for 30 seconds.
  1105. .RE
  1106. .P
  1107. .SS "Differences from Readnews"
  1108. .. .RS 5
  1109. Most of the readnews commands have
  1110. .B vnews
  1111. counterparts and vice versa.
  1112. Commands are not present in
  1113. .B Vnews
  1114. include:
  1115. .TP 10
  1116. .B d
  1117. The digest command is not yet implemented.
  1118. .TP 10
  1119. .B K
  1120. You can get the same effect by using the
  1121. .B n
  1122. command with a huge count.
  1123. .TP 10
  1124. .B P
  1125. Use "N-".
  1126. .TP 10
  1127. .B X
  1128. This should be a separate program.
  1129. .P
  1130. .B Vnews
  1131. has commands for moving around in the article which readnews does not have
  1132. since they aren't applicable.
  1133. It also has a
  1134. .I parent
  1135. command which will go to the article that the current article is a followup
  1136. to, and a
  1137. .I write
  1138. command that writes out the body of an article without the header.
  1139. .P
  1140. You can refer to the current article from the shell or while writing a
  1141. followup as $A.
  1142. .P
  1143. You can use the
  1144. .B n
  1145. command to delete articles while looking at the index page.
  1146. .. .P
  1147. .. The
  1148. .. .I decrypt
  1149. .. command always does rotation 13.
  1150. .. .P
  1151. .. The algorithm for handling articles posted to multiple groups is different;
  1152. .. .B vnews
  1153. .. attempts to show you these articles only once even if you quit in the
  1154. .. middle of running
  1155. .. .B vnews
  1156. .. and resume it later.
  1157. .. .P
  1158. .P
  1159. .SS "Article selection algorithm"
  1160. .P
  1161. Newsgroups are shown in the order that they appear in the .newsrc file.
  1162. Any newsgroups which contain articles but do not appear in the .newsrc
  1163. file are appended to the .newsrc file in the order in which they appear
  1164. in the groupfile file.  (This means that if the -x option is specified
  1165. you will be shown newsgroups in the order in which they appear in the
  1166. groupfile file.)
  1167. .P
  1168. Vnews has a newsgroup stack which allows the commands
  1169. .BR N ,
  1170. .BR p ,
  1171. and
  1172. .B <
  1173. to save the current newsgroup.
  1174. Only real newsgroups are pushed; not single articles fetched using the
  1175. .B p
  1176. and
  1177. .B <
  1178. commands.
  1179. The phrase "advance to the next newsgroup" always means
  1180. "pop the newsgroup stack if it is nonempty; otherwise go to the next
  1181. newsgroup in the .newsrc file."
  1182. .P
  1183. Within a newsgroup, articles are grouped by discussion.  Within a
  1184. discussion, articles are sorted by posting date.  Discussions are
  1185. sorted by the posting date of the first new article in the discussion.
  1186. .P
  1187. Articles within a newsgroup are numbered by
  1188. .B vnews
  1189. so that you can refer to them.
  1190. These numbers are not related to the names of the files containing the
  1191. articles.
  1192. .P
  1193. An article is normally considered to be read if you have
  1194. seen the end of it.
  1195. The
  1196. .B n
  1197. and
  1198. .B e
  1199. commands allow you to explicitly mark an article as read or unread.
  1200. .P
  1201. When an article has been posted to multiple group, vnews will show
  1202. the article only in the leftmost newsgroup in the newsgroup list that
  1203. you subscribe to.
  1204. .P
  1205. .SS "Commands"
  1206. .P
  1207. Each
  1208. .B vnews
  1209. command may be preceded by a \fIcount\fR.
  1210. Some commands use the \fIcount\fR; others ignore it.
  1211. If the \fIcount\fR is omitted, it defaults to one.
  1212. Some commands prompt for an argument on the secondary prompt line.
  1213. The argument is terminated by a return.
  1214. Standard
  1215. .SM UNIX
  1216. erase and kill processing is performed on commands being entered.
  1217. An interrupt (delete or break) or a CONTROL-G
  1218. gets you out of any partially entered command.
  1219. .P
  1220. The following commands exist:
  1221. .TP 10
  1222. .B SP
  1223. A space scrolls forward.
  1224. .. [Normally space advances an entire page, but if there are
  1225. .. only one or two more lines left in the current article,
  1226. .. space will simply advance the article the one or two lines
  1227. .. required to show the rest of the article.]  \
  1228. ..
  1229. If you are at the end of a article, it advances to the next article.
  1230. If you are at the end of the index, it leaves the index and displays
  1231. the current article.
  1232. .TP 10
  1233. .B "^B"
  1234. A CONTROL-B goes backwards \fIcount\fR pages.
  1235. .TP 10
  1236. .B "^D"
  1237. Go forward \fIcount\fR half pages.
  1238. .TP 10
  1239. .BR "^N" " or " "^Y"
  1240. Go forwards \fIcount\fR lines.
  1241. .TP 10
  1242. .BR "^P" " or " "^Z"
  1243. Go backwards \fIcount\fR lines.
  1244. .TP 10
  1245. .B "^U"
  1246. Go backwards \fIcount\fR half pages.
  1247. .TP 10
  1248. .B a
  1249. Switch the display between the index and the current article.
  1250. The index is identical to that produced by the
  1251. .I readnews
  1252. .B a
  1253. command except that articles which have been read are marked with "D".
  1254. All
  1255. .I vnews
  1256. commands except
  1257. .B D
  1258. are functional on the index; the article scrolling commands
  1259. scroll the index and all other commands apply to the current article.
  1260. [In particular, you can type a sequence of
  1261. .BR n 's
  1262. and
  1263. .BR e 's
  1264. on the index page to quickly get rid of uninteresting articles.]
  1265. .TP 10
  1266. .BR "b" " or " "-"
  1267. Subtract \fIcount\fR from the current article number.
  1268. If the result would be less than one, an error message is printed.
  1269. .. [It would be nice to have a command which
  1270. .. always go back to the previous article like the 'b' command in 2.10.]
  1271. .TP 10
  1272. .B c
  1273. Cancel the current article.
  1274. .B Vnews
  1275. prompts for confirmation before canceling the article.
  1276. Carriage return or 'y' means yes; anything else means no.
  1277. .TP 10
  1278. .B "D"
  1279. Decrypts a joke.
  1280. It only handles rot 13 jokes.
  1281. The
  1282. .B D
  1283. command is a toggle; typing another D re-encrypts the joke.
  1284. .TP 10
  1285. .B e
  1286. Mark the current article as not read and advance \fIcount\fR unread articles.
  1287. If there are fewer than \fIcount\fR unread articles left in the
  1288. newsgroup, advance to the next newsgroup.
  1289. .TP 10
  1290. .B h
  1291. Go back to the top of the article and display only the header.
  1292. .TP 10
  1293. .B H
  1294. Show the complete header.
  1295. Typing
  1296. .B l
  1297. (or almost any command)
  1298. will restore the original display.
  1299. .. [I'm still not convinced of the value of this command,
  1300. .. but other people seem to be...]
  1301. .TP 10
  1302. .BR "l" " or " "d"
  1303. Causes the current article to be displayed.
  1304. Display of the current article is turned off by commands which scramble the
  1305. screen
  1306. .RB "(" "!" ","
  1307. .BR "f" ","
  1308. and
  1309. .BR "r" ")."
  1310. .. My feeling here is that the user frequently wants to respond to an article
  1311. .. and then go on to the next article; she/he shouldn't be forced to wait while
  1312. .. the current article is rewritten to the screen.
  1313. The
  1314. .B l
  1315. command will also redisplay the article if the help message
  1316. or detailed header is currently
  1317. displayed.
  1318. The (feeble) mnemonic significance of
  1319. .B l
  1320. is that is similar to control-L, which redraws the screen.
  1321. .. [Blanking out the article display has been of rather questionable value
  1322. .. since release 2.10, when checks for pending input were added.
  1323. .. However, the 3B20 hardware buffers about 256 characters of output,
  1324. .. and some versions of UNIX may not support checks for pending input,
  1325. .. so I have left it in.  It is straightforward enough if you are used to
  1326. .. it.]
  1327. .TP 10
  1328. .B n
  1329. Mark the current article as read.
  1330. Then advance by \fIcount\fR unread articles, marking the skipped articles as
  1331. read.  If there are fewer than \fIcount\fR unread articles left in the
  1332. newsgroup, mark the unread articles as read and advance to the next
  1333. newsgroup.
  1334. .TP 10
  1335. .B N
  1336. Go to a different newsgroup.
  1337. You are prompted for a newsgroup name.
  1338. .IP "" 10
  1339. There are two special cases.
  1340. A null newsgroup name 
  1341. it advances to the next group.
  1342. The name "\-" pops everything off the newsgroup stack and then backs
  1343. up to the previous group in the .newsrc file.
  1344. [This isn't implemented properly yet; the
  1345. current code backs up to the previous group that contains articles,
  1346. just like in 2.10.
  1347. It should restore the previously read group.]
  1348. .IP "" 10
  1349. Otherwise, the
  1350. .B N
  1351. command
  1352. .. discards everything on the newsgroup stack,
  1353. pushes the current group onto the stack and goes to the specified group.
  1354. Both read and unread articles are included in the index.
  1355. However, the
  1356. .B n
  1357. and
  1358. .B e
  1359. commands will skip over the articles that have been already read.
  1360. .TP 10
  1361. .B p
  1362. Gets you the parent article (the article that the current article is a
  1363. followup to).
  1364. If no
  1365. .B References:
  1366. line exists for the current article then the title of the article will
  1367. be used to guess the parent; of course this guess may be wrong.
  1368. .TP 10
  1369. .B "q"
  1370. Quit. The .newsrc file is updated.
  1371. .TP 10
  1372. .B r
  1373. Reply to the author of the article.
  1374. See the
  1375. .B f
  1376. command for details.
  1377. .TP 10
  1378. .B f
  1379. Post a followup article.
  1380. No warning messages are printed.
  1381. [I despise them, and so far nobody has asked for them.]
  1382. If the article was posted to a moderated group, the followup is mailed to the
  1383. sender of the article.
  1384. .IP "" 10
  1385. Both \fIreply\fR and \fIfollowup\fR work similarly.
  1386. A file containing a header is created and an editor is invoked on the file.
  1387. You can modify the header as well as enter the body of the article.
  1388. The environment variable
  1389. .B "$A"
  1390. is set to the current article in case you want to refer to it or quote it in
  1391. your response.
  1392. .B "$A"
  1393. will also be passed as an argument to the editor.
  1394. .IP "" 10
  1395. If you plan to quote extensively from the article you are writing a followup
  1396. to, you may find it easiest to include the entire body of the parent article
  1397. and delete the parts you don't want.  If you use EMACS (either Gosling's
  1398. or Warren Montgomery's), there is a macro called gparent (get parent article)
  1399. which is normally bound to ^X^Y
  1400. that reads in the body of the parent article, prefacing each line with a
  1401. .BR > .
  1402. If you use a different editor, such as
  1403. .BR ed (1),
  1404. there is a shell procedure which will accomplish the same task if your
  1405. editor has the ability to capture the output of a command.  In
  1406. .BR ed ,
  1407. type
  1408. .BR "r\ !gparent" .
  1409. .. The second argument to the editor is
  1410. .. .IR "$A" ,
  1411. .. so that if you run Gosling's Emacs, the article will automaticly appear in
  1412. .. the second window.
  1413. .IP "" 10
  1414. If you change your mind about replying or posting a followup article, exit
  1415. the editor without changing the file.
  1416. .. [A message will appear on the screen to inform you that the response was not
  1417. .. mailed/posted.]
  1418. If you change your mind about whether you should have used an
  1419. .B f
  1420. or
  1421. .B r
  1422. command, edit the first line of the followup.
  1423. .. .IP "" 10
  1424. .. [Vnews forks off a process to deliver the reply or followup.
  1425. .. In order to avoid messing up the screen, errors are reported by mail.]
  1426. .TP 10
  1427. .B s
  1428. Prompts for a filename and writes the article to the file.
  1429. Depending on how the netnews administrator set up your system, the article
  1430. may have a "From " line in front of it to allow the file to be read using
  1431. your mail program.
  1432. .IP "" 10
  1433. The
  1434. .I save
  1435. and
  1436. .I write
  1437. commands normally append to the specified file,
  1438. but if
  1439. .I count
  1440. is zero any existing file is overwritten.
  1441. .IP "" 10
  1442. If the filename does not begin with a slash
  1443. vnews runs through the following list of kludges.
  1444. If the filename is omitted, it defaults to "Articles".
  1445. A leading "~/" on the filename is replaced by the name of your login directory.
  1446. Finally, if the filename is does not begin with a slash and the environment
  1447. variable $NEWSBOX is set, then any "%s" in $NEWSBOX replaced
  1448. by the current newsgroup and the result is prepended to the filename.
  1449. .. Users frequently set $NEWSBOX to the name of their login directly.
  1450. .TP 10
  1451. .BR "s|" ", " "w|"
  1452. Read a command and pipe the article into it.
  1453. The article is formated identically to the
  1454. .B s
  1455. and
  1456. .B w
  1457. commands.
  1458. .TP 10
  1459. .B ud
  1460. Unsubscribe to the current discussion.
  1461. [This command is still somewhat experimental.
  1462. In particular, if you unsubscribe to very may groups reading and writing
  1463. the .newsrc file becomes very slow.]
  1464. .TP 10
  1465. .BR ug " or " un
  1466. If \fIcount\fR is nonzero, unsubscribe to the current group and advance
  1467. to the next group.
  1468. The group will no longer be shown to you unless you specificly ask for
  1469. it with the
  1470. .B N
  1471. command.
  1472. If \fIcount\fR is zero, resubscribe to the current group.
  1473. .. [This is a two character command to ensure that it is not typed accidentally
  1474. .. and to leave room for other types of unsubscribes (e. g. unsubscribe to
  1475. .. discussion).]
  1476. .TP 10
  1477. .B v
  1478. Print netnews version.
  1479. .TP 10
  1480. .B w
  1481. Like
  1482. .B s
  1483. except that the article header is not written.
  1484. .TP 10
  1485. .B "+"
  1486. Add \fIcount\fR to the current article number.
  1487. If this would go beyond the end of the current newsgroup an error
  1488. message is printed.
  1489. .TP 10
  1490. .B "^\e"
  1491. When sent a quit signal,
  1492. .B vnews
  1493. terminates without updating .newsrc.
  1494. Depending on how your system administrator set up vnews
  1495. it may or may not generate a core dump.
  1496. [This command is intentionally hard to type.]
  1497. .TP 10
  1498. .B "!"
  1499. Prompts for a
  1500. .SM UNIX
  1501. command and passes it to the shell.
  1502. The environment variable
  1503. .I $A
  1504. is set to the name of the file containing the current article.
  1505. If the last character of the command is a "&", then the "&" is deleted and
  1506. the command is run in the background with stdin, stdout and stderr redirected
  1507. to /dev/null.
  1508. If the command is missing, the shell is invoked.
  1509. Use the
  1510. .B l
  1511. command (or essentially any other command) to turn on the display after the
  1512. program terminates.
  1513. .. .TP 10
  1514. .. .B "#"
  1515. .. Prints the numbers of the current article and the last article in the current
  1516. .. newsgroup.
  1517. .TP 10
  1518. .B "^L"
  1519. Redraws the screen.
  1520. CONTROL-L is not a real command; it may be typed while in the middle of
  1521. entering another command.
  1522. .TP 10
  1523. .B "<"
  1524. Prompts for an article ID or the rest of a message ID,
  1525. and displays the article if it exists.
  1526. .TP 10
  1527. .B CR
  1528. Carriage return goes to the article numbered \fIcount\fR in the current
  1529. newsgroup if count is specified.
  1530. If count is omitted, then a carriage return is treated like a space.
  1531. .TP 10
  1532. .B "?"
  1533. Displays a synopsis of commands.
  1534. The synopsis will be removed from the screen when the next command is
  1535. executed.
  1536. If you want to remove the synopsis without doing anything else, use the
  1537. .B l
  1538. command.
  1539. .SH AUTHOR
  1540. Kenneth Almquist
  1541. .br
  1542. {harpo, ihnp4, burl, akgua}!hou3c!ka.
  1543. .SH BUGS
  1544. Netnews release 2.10.2 stores dates in GMT, which is probably not
  1545. what the user expects.
  1546. .P
  1547. As with other visual interfaces,
  1548. .B vnews
  1549. does not handle typing errors gracefully.
  1550. Perhaps there should be an "undo" command.
  1551. .P
  1552. No "digest" command is provided.
  1553. .P
  1554. The
  1555. .I save
  1556. and
  1557. .I write
  1558. commands should create nonexistent directories.
  1559. !E!O!F!
  1560.  
  1561. cat > vnews/vnews.h <<\!E!O!F!
  1562. #ifndef EXTERN
  1563. #define EXTERN extern
  1564. #endif
  1565.  
  1566. #ifdef HCURSES        /* Mark Horton's version of curses */
  1567. #define CURSES
  1568. #endif
  1569. #ifdef ACURSES        /* Ken Arnold's version of curses */
  1570. #define CURSES
  1571. #endif
  1572. #ifdef CURSES
  1573. #include <curses.h>
  1574. #define ROWS LINES
  1575. int hasscroll = 0;
  1576. #else
  1577. #endif CURSES
  1578.  
  1579. #include <errno.h>
  1580. #include "rparams.h"
  1581. #include "artfile.h"
  1582. #include "newsrc.h"
  1583.  
  1584. #ifdef CURSES
  1585. #undef LINES
  1586. #endif
  1587.  
  1588. #define ARTWLEN    (ROWS-2)/* number of lines used to display article */
  1589. #ifdef STATTOP
  1590. #define SECPRLEN 81    /* length of secondary prompter */
  1591. #else
  1592. #define SECPRLEN 100    /* length of secondary prompter */
  1593. #endif
  1594. #define INTR    '\7'    /* interrupt character */
  1595. /* prun flags */
  1596. #define CWAIT    0001    /* type "continue?" and wait for return */
  1597. #define BKGRND    0002    /* run process in the background */
  1598. /* values of curflag */
  1599. #define CURP1    1    /* cursor after prompt */
  1600. #define CURP2    2    /* cursor after secondary prompt */
  1601. #define CURHOME    3    /* cursor at home position */
  1602. /* types of article lists */
  1603. #define SVNEWSGROUP 0    /* a newsgroup was saved (must be zero) */
  1604. #define SVARTICLE   1    /* a sigle article was saved */
  1605. #if BSDREL >= 42
  1606. #define sigset signal
  1607. #endif
  1608.  
  1609. struct window {
  1610.     int w_artlin;            /* line to appear at top of window */
  1611.     int w_svartlin;            /* line currently at top of window */
  1612.     int (*w_dump)();        /* routine to dump window to screen */
  1613.     int w_force;            /* set to force window update */
  1614. };
  1615.  
  1616.  
  1617. struct svartlist {
  1618.     int al_type;            /* type of article list */
  1619.     int al_tfoffset;            /* offset into temp file where saved */
  1620. };
  1621.  
  1622. struct artinfo {
  1623.     ARTNO i_artnum;        /* number in spool directory */
  1624.     long  i_subtime;    /* date/time article posted, in internal GMT */
  1625.     long  i_basetime;    /* subtime of base article */
  1626.     DPTR  i_dptr;        /* artfile entry */
  1627.     long  i_nlines;        /* lines: header (number of lines) */
  1628.     char  i_title[37];    /* subject */
  1629.     char  i_from[31];    /* sender of message */
  1630. };
  1631.  
  1632. extern int errno;
  1633.  
  1634. /* terminal handler stuff */
  1635. extern int _junked;
  1636. #define clearok(xxx, flag) _junked = flag
  1637. extern int COLS;
  1638. extern int ROWS;
  1639. extern int hasscroll;
  1640. extern int needbeep;
  1641.  
  1642. EXTERN int ngrp;            /* set on entry to new group    */
  1643. EXTERN char filename[BUFLEN];        /* file containing current art    */
  1644.  
  1645. EXTERN FILE *tfp;            /* temporary file */
  1646. EXTERN long artbody;            /* offset of body into article */
  1647. EXTERN long artlength;            /* article length in bytes */
  1648. EXTERN int quitflg;            /* if set, then quit */
  1649. EXTERN int hupflag;            /* set when hangup signal received */
  1650. EXTERN int erased;            /* current article has been erased */
  1651. EXTERN int artlines;            /* # lines in article body */
  1652. EXTERN int artread;            /* entire article has been read */
  1653. EXTERN int hdrstart;            /* beginning of header */
  1654. EXTERN int hdrend;            /* end of header */
  1655. EXTERN int lastlin;            /* number of lines in tempfile */
  1656. EXTERN int tflinno;            /* next line in tempfile */
  1657. EXTERN int tfoffset;            /* offset of article in temp file */
  1658. EXTERN char secpr[SECPRLEN];        /* secondary prompt */
  1659. EXTERN char prompt[30];            /* prompter */
  1660. EXTERN short hdronly;            /* if set, only print article header */
  1661. EXTERN short curflag;            /* where to locate cursor */
  1662. EXTERN char timestr[20];        /* current time */
  1663. EXTERN int ismail;            /* true if user has mail */
  1664. EXTERN char *mailf;            /* user's mail file */
  1665. EXTERN int atend;            /* set if at end of article */
  1666. EXTERN char cerase;            /* erase character */
  1667. EXTERN char ckill;            /* kill character */
  1668. EXTERN int ospeed;            /* terminal speed */
  1669. EXTERN int pstatus;            /* status return from process */
  1670. EXTERN int indexpg;            /* set if on index page */
  1671. EXTERN struct window *curwin;        /* current window */
  1672. EXTERN struct window *nextwin;        /* new window */
  1673. #ifdef DIGPAGE
  1674. EXTERN int endsuba;            /* end of sub-article in digest */
  1675. #endif
  1676. #ifdef MYDEBUG
  1677. EXTERN FILE *debugf;            /* file to write debugging info on */
  1678. #endif
  1679. extern struct window artwin;        /* window displaying article */
  1680. extern struct window indexwin;        /* window containing article index */
  1681. extern struct window helpwin;        /* help windown */
  1682. extern struct window emptywin;        /* blank screen */
  1683. extern struct window hdrwin;        /* window containing header */
  1684.  
  1685. EXTERN struct artinfo *thisng;        /* articles in this newsgroup */
  1686. EXTERN int numthisng;            /* # of articles in above array */
  1687. EXTERN int curind;            /* how far we've gotten into thisng */
  1688. EXTERN struct artinfo *curart;        /* how far we've gotten into thisng */
  1689.  
  1690.  
  1691. EXTERN long pngsize;            /* Printing ngsize        */
  1692. EXTERN int  news;
  1693. EXTERN struct arthead h;        /* header of current article    */
  1694. EXTERN int dgest;
  1695. EXTERN FILE *fp;            /* current article to be printed*/
  1696. EXTERN int debugging;            /* debugging turned on */
  1697. !E!O!F!
  1698.  
  1699. cat > vnews/vnews.help <<\!E!O!F!
  1700. Vnews commands:    (each may be preceded by a non-negative count)
  1701.  
  1702. SP  Next page or article                D   Decrypt a rot 13 joke
  1703. n   Go to next article                  CR  Go to article numbered count
  1704. e   Mark current article as unread      <   Go to article with given ID
  1705. +   Go forwards count articles          p   Go to parent article
  1706. b   Go to previous article              ug  Unsubscribe to this group
  1707. ^B  Go backwards count pages            ud  Unsubscribe to discussion
  1708. ^N  Go forward count lines              ^L  Redraw screen
  1709. ^P  Go backwards count lines            v   Print netnews version
  1710. ^D  Go forward half a page              c   Cancel the current article
  1711. a   Switch to/from article index        q   Quit
  1712. H   Display article header              ^\  Quit without updating .newsrc
  1713. !   Escape to shell
  1714. r   Reply to article
  1715. f   Post a followup article
  1716. l   Display article (use after !, r, f, or ?)
  1717. s   Save article in file
  1718. w   Save without header
  1719. N   Go to newsgroup (next is default)
  1720.  
  1721. [Press l to see article again]
  1722. !E!O!F!
  1723.  
  1724. cat > vnews/vreadr.c <<\!E!O!F!
  1725. /*
  1726.  * Readr - visual news interface.
  1727.  */
  1728.  
  1729. #define EXTERN
  1730. #include "vnews.h"
  1731.  
  1732. #define PIPECHAR '|'    /* indicate save command should pipe to program */
  1733. #define META    0200    /* meta chatacter bit (as in emacs) */
  1734. #define UNSUB    0400    /* unsubscribe bit (indicates 'u' command) */
  1735. /* flags for vsave routine */
  1736. #define SVHEAD    01    /* write out article header */
  1737. #define OVWRITE    02    /* overwrite the file if it already exists */
  1738.  
  1739. char *getmailname();
  1740. int onint(), onquit(), onhup();
  1741. int onstop();
  1742.  
  1743. static char tfname[] = "/tmp/vnXXXXXX";    /* name of temp file */
  1744. static char prbuf[SECPRLEN+1];        /* buf for getting command args */
  1745. static int errclean;            /* clean up on error        */
  1746. static char *bptr;            /* temp pointer.        */
  1747. extern struct svartlist *alptr;        /* article list stack        */
  1748.  
  1749.  
  1750. readr()
  1751. {
  1752. #ifdef    SIGCONT
  1753.     int (*ocont)();
  1754. #endif
  1755.     FILE *popen();
  1756.  
  1757. #ifdef MYDEBUG
  1758.     debugf = fopen("DEBUG", "w");
  1759.     setbuf(debugf, NULL);
  1760. #endif
  1761.  
  1762. /*
  1763.     fp = popen("pwd", "r");
  1764.     if (fp == NULL)
  1765.         xerror("Cannot fork/exec/popen pwd!\n");
  1766.     fgets(curdir, sizeof curdir, fp);
  1767.     pclose(fp);
  1768.     nstrip(curdir);
  1769. */
  1770.  
  1771.     mktemp(tfname);
  1772.     if ((tfp = fopen(tfname, "w+")) == NULL)
  1773.         xerror("Can't create temp file");
  1774.     unlink(tfname);
  1775.     mailf = getmailname();
  1776.  
  1777.     /* loop reading articles. */
  1778.     fp = NULL;
  1779.     curwin = Aflag? &indexwin : &artwin;
  1780.     if (getnxtng(FORWARD)) {
  1781.         fprintf(stderr, "No news.\n");
  1782.         return;
  1783.     }
  1784.  
  1785.     ttysave();
  1786.     signal(SIGINT, onint);
  1787.     signal(SIGQUIT, onquit);
  1788.     signal(SIGHUP, onhup);
  1789.     ttyraw();
  1790.     errclean = 1;
  1791.     timer();
  1792.  
  1793.     quitflg = 0;
  1794.     while (quitflg == 0) {        /* command loop */
  1795.         if (hupflag) {
  1796.             errclean = 0;
  1797.             return;
  1798.         }
  1799.         if (!indexpg)
  1800.             openart();
  1801.         vcmd();
  1802.     }
  1803.     errclean = 0;
  1804.     botscreen();
  1805.     ttycooked();
  1806. }
  1807.  
  1808. /*
  1809.  * Read and execute a command.
  1810.  */
  1811.  
  1812. vcmd() {
  1813.     register c;
  1814.     register char *p, *q;
  1815.     char *promptp;
  1816.     char *cmdp;
  1817.     char cmd[20];
  1818.     int count;
  1819.     int countset;
  1820.  
  1821.     if (indexpg) {
  1822.         atend = numthisng + 2 <= indexwin.w_artlin + ARTWLEN;
  1823.     } else {
  1824.         appfile(fp, artwin.w_artlin + ARTWLEN + 1);
  1825.         atend = artlines <= artwin.w_artlin + ARTWLEN
  1826.              && (! hdronly || artlines <= hdrend);
  1827.     }
  1828.     setprompt();
  1829.     if (atend && ! indexpg && ! erased)
  1830.         setnew(0);        /* article read */
  1831.     curflag = CURP1;
  1832.     promptp = prompt + strlen(prompt);
  1833.     cmdp = cmd;
  1834.     do {
  1835.         c = vgetc();
  1836.         if (c == cerase) {
  1837.             if (cmdp > cmd)
  1838.                 cmdp--;
  1839.         } else if (c == ckill) {
  1840.             cmdp = cmd;
  1841.         } else {
  1842.             if (c == '\\') {
  1843.                 strcat(prompt, "\\");
  1844.                 c = vgetc();
  1845.             }
  1846.             if (c == INTR) {
  1847.                 secpr[0] = '\0';
  1848.                 cmdp = cmd;
  1849.             } else {
  1850.                 *cmdp++ = c;
  1851.             }
  1852.         }
  1853.         *cmdp = '\0';
  1854.  
  1855.         p = cmd;
  1856.         q = promptp;
  1857.         countset = 0;
  1858.         count = 0;
  1859.         while ((c = *p) >= '0' && c <= '9') {
  1860.             if (p > cmd + 5)
  1861.                 *--cmdp = '\0';        /* limit to 5 digits */
  1862.             else
  1863.                 *q++ = c;
  1864.             count = (count * 10) + (c - '0');
  1865.             countset = 1;
  1866.             p++;
  1867.         }
  1868.         c = 0;
  1869.         if (*p == 'u') {
  1870.             c = UNSUB;
  1871.             *q++ = 'u';
  1872.             p++;
  1873.         }
  1874.         if (*p == '\033') {            /* escape */
  1875.             c |= META;
  1876.             *q++ = 'M';
  1877.             *q++ = '-';
  1878.             p++;
  1879.         }
  1880.         *q = '\0';
  1881.         c |= *p;
  1882.     } while (p >= cmdp);
  1883.  
  1884.     secpr[0] = '\0';
  1885.     if (countset == 0)
  1886.         count = 1;
  1887.     nextwin = NULL;
  1888.     docmd(c, count, countset);
  1889.     if (nextwin != NULL)
  1890.         curwin = nextwin;
  1891.     else if (indexpg)
  1892.         curwin = &indexwin;
  1893.     else
  1894.         curwin = &artwin;
  1895.     if (artwin.w_artlin > hdrstart && hdronly)
  1896.         hdronly = 0, artwin.w_force |= 1;
  1897. }
  1898.  
  1899.  
  1900. /*
  1901.  * Generate the prompt (percentage of article read).
  1902.  */
  1903. setprompt() {
  1904.     int percent;
  1905.  
  1906.     if (atend)
  1907.         strcpy(prompt, "100% ");
  1908.     else if (hdronly && ! indexpg)
  1909.         strcpy(prompt, "0% ");
  1910.     else {
  1911.         if (indexpg) {
  1912.             percent = (800L * (indexwin.w_artlin + ARTWLEN))
  1913.                 / (numthisng + 2);
  1914.         } else {
  1915.             percent = ((800 * (ftell(fp) - artbody)) / (artlength - artbody)
  1916.                 * (artwin.w_artlin + ARTWLEN)) / lastlin;
  1917.         }
  1918.         percent = (percent + 4) >> 3;
  1919.         if (percent > 99)
  1920.             percent = 99;
  1921.         strcpy(prompt, "00% ");
  1922.         prompt[0] = percent / 10 + '0';
  1923.         prompt[1] = percent % 10 + '0';
  1924.     }
  1925. }        
  1926.  
  1927.  
  1928. /*
  1929.  * Process one command, which has already been typed in.
  1930.  */
  1931. docmd(c, count, countset)
  1932. {
  1933.     int i;
  1934.     char *ptr1;
  1935.     char *findhist();
  1936.  
  1937.     switch (c) {
  1938.  
  1939.     /* Turn on/off debugging. */
  1940.     case 'z':
  1941.         debugging = count;
  1942.         msg("debugging level %d", debugging);
  1943.         break;
  1944.  
  1945.     /* No/Next.  Go on to next article. */
  1946.     case 'n':
  1947.         setnew(0);
  1948.         nextart(count, 1);
  1949.         break;
  1950.  
  1951.     /* go to specific article, or treat like space */
  1952.     case '\n':
  1953.         if (countset) {
  1954.             setartno(count);
  1955.             break;
  1956.         }
  1957.         /* else fall through */
  1958.  
  1959.     /* Show more of current article, or advance to next article */
  1960.     case ' ':
  1961.         if (indexpg) {
  1962.             if (atend)
  1963.                 indexwin.w_artlin = 0, indexpg = 0;
  1964.             else if (hasscroll && numthisng - indexwin.w_artlin <= ARTWLEN)
  1965.                 scroll(numthisng + 2 - indexwin.w_artlin - ARTWLEN);
  1966.             else
  1967.                 scroll(ARTWLEN);
  1968.         } else {
  1969.             if (atend)
  1970.                 nextart(1, 1);
  1971.             else if (hdronly) {
  1972.                 hdronly = 0;
  1973.                 artwin.w_force |= 1;
  1974.                 if (hasscroll)
  1975.                     scroll(hdrstart - artwin.w_artlin);}
  1976.             else if ((appfile(fp, artwin.w_artlin + 2 * ARTWLEN), artread)
  1977.              && hasscroll && artlines - artwin.w_artlin <= ARTWLEN + 2)
  1978.                 scroll(artlines - artwin.w_artlin - ARTWLEN);
  1979.             else
  1980.                 scroll(ARTWLEN);
  1981.         }
  1982.         break;
  1983.  
  1984.  
  1985.     /* All headers: show headers for this newsgroup */
  1986.     case 'a':
  1987.         indexpg = ! indexpg;
  1988.         break;
  1989.  
  1990.     /* Back up count pages */
  1991.     case META|'v':
  1992.     case '\2':    /* Control-B */
  1993.         scroll(-ARTWLEN * count);
  1994.         break;
  1995.  
  1996.     /* forward half a page */
  1997.     case '\4':    /* Control-D, as in vi */
  1998.         scroll(ARTWLEN/2 * count);
  1999.         break;
  2000.  
  2001.     /* backward half a page */
  2002.     case '\25':    /* Control-U */
  2003.         scroll(-ARTWLEN/2 * count);
  2004.         break;
  2005.  
  2006.     /* forward count lines */
  2007.     case '\16':    /* Control-N */
  2008.     case '\32':    /* Control-Z */
  2009.         scroll(count);
  2010.         break;
  2011.  
  2012.     /* bakcwards count lines */
  2013.     case '\20':    /* Control-P */
  2014.     case '\31':    /* Control-Y */
  2015.         scroll(-count);
  2016.         break;
  2017.  
  2018.     /* Turn displaying of article back on */
  2019.     case 'l':
  2020.     case 'd':
  2021.         /* this now happens automaticly */
  2022.         break;
  2023.  
  2024.     /* display header */
  2025.     case 'h':
  2026.         scroll(hdrstart - curwin->w_artlin);
  2027.         hdronly = 1;
  2028.         artwin.w_force |= 1;
  2029.         break;
  2030.  
  2031.     /*
  2032.      * Unsubscribe to the newsgroup and go on to next group
  2033.      */
  2034.     case UNSUB|'g':
  2035.     case UNSUB|'n':
  2036.         if (count) {
  2037.             curng->ng_unsub = 1;
  2038.             if (getnxtng(FORWARD))
  2039.                 quitflg++;
  2040.         } else /* resubscribe */
  2041.             curng->ng_unsub = 0;
  2042.         break;
  2043.  
  2044.         /* unsubscribe to the current discussion */
  2045.     case UNSUB|'d':
  2046.         ud_command();
  2047.         break;
  2048.  
  2049.     case UNSUB|'a': ptr1 = "author";     goto badusub;
  2050.     case UNSUB|'s': ptr1 = "site";       goto badusub;
  2051. badusub:    msg("Unsubscribing to this %s: not implemented", ptr1);
  2052.         break;
  2053.  
  2054.         /* Print the current version of news */
  2055.     case 'v':
  2056.         msg("News version: %s", news_version);
  2057.         nextwin = curwin;
  2058.         break;
  2059.  
  2060.  
  2061.         /* decrypt joke */
  2062.     case 'D': 
  2063.         if (indexpg)
  2064.             goto illegal;
  2065.         appfile(fp, 32767);
  2066.         for (i = hdrend ; i < artlines ; i++) {
  2067.             register char c, *p;
  2068.             tfget(bfr, i);
  2069.             for (p = bfr ; (c = *p) != '\0' ; p++) {
  2070.                 if (c >= 'a' && c <= 'z')
  2071.                     *p = (c - 'a' + 13) % 26 + 'a';
  2072.                 else if (c >= 'A' && c <= 'Z')
  2073.                     *p = (c - 'A' + 13) % 26 + 'A';
  2074.             }
  2075.             tfput(bfr, i);
  2076.         }
  2077.         artwin.w_force |= 1;
  2078.         hdronly = 0;
  2079.         break;
  2080.  
  2081.         /* write out the article someplace */
  2082.     case 's':
  2083.     case 'w':
  2084.         {
  2085.         int wflags;
  2086.  
  2087.         nextwin = curwin;
  2088.         if (openart())
  2089.             break;
  2090.         wflags = 0;
  2091.         if (c == 's')
  2092.             wflags |= SVHEAD;
  2093.         if (count != 1)
  2094.             wflags |= OVWRITE;
  2095.         msg("file: ");
  2096.         curflag = CURP2;
  2097.         while ((c = vgetc()) == ' ');
  2098.         if (c == INTR) {
  2099.             secpr[0] = '\0';
  2100.             break;
  2101.         }
  2102.         if (c == '|') {
  2103.             prbuf[0] = PIPECHAR;
  2104.             if (prget("| ", prbuf+1))
  2105.                 break;
  2106.         } else {
  2107.             pushback(c);
  2108.             if (prget("file: ", prbuf))
  2109.                 break;
  2110.         }
  2111.         bptr = prbuf;
  2112.         if (*bptr != PIPECHAR && *bptr != '/') {
  2113.             char    hetyped[BUFLEN];
  2114.             char    *boxptr;
  2115.             strcpy(hetyped, bptr);
  2116.             if (boxptr = getenv("NEWSBOX"))
  2117.                 if (index(boxptr, '%'))
  2118.                     sprintf(bptr, boxptr, curng->ng_name);
  2119.                 else
  2120.                     strcpy(bptr, boxptr);
  2121.             else if (hetyped[0] == '~' && hetyped[1] == '/') {
  2122.                 strcpy(hetyped, bptr+2);
  2123.                 strcpy(bptr, userhome);
  2124.             } else
  2125.                 bptr[0] = '\0';
  2126.             if (bptr[0])
  2127.                 strcat(bptr, "/");
  2128.             if (hetyped[0] != '\0')
  2129.                 strcat(bptr, hetyped);
  2130.             else
  2131.                 strcat(bptr, "Articles");
  2132.         }
  2133.         vsave(bptr, wflags);
  2134.         }
  2135.         break;
  2136.  
  2137.         /* back up  */
  2138.     case '-':
  2139.     case 'b':
  2140.         if (curind <= count) {
  2141.             msg("Can't back across newsgroup boundary.");
  2142.             break;
  2143.         }
  2144.         setartno(curind - count);
  2145.         break;
  2146.  
  2147.         /* skip forwards */
  2148.     case '+':
  2149.         /* this may not be what we want */
  2150.         setartno(curind + count);
  2151.         break;
  2152.  
  2153.     /* exit - time updated to that of most recently read article */
  2154.     case 'q':
  2155.         quitflg = 1;
  2156.         break;
  2157.  
  2158.     /* cancel the article. */
  2159.     case 'c':
  2160.         strcpy(prompt, "cancel? ");    /* be sure not a typo */
  2161.         if ((c = vgetc()) != '\n' && c != 'y')
  2162.             break;
  2163.         cancel_command();
  2164.         break;
  2165.  
  2166.     /* escape to shell */
  2167.     case '!':
  2168.         {
  2169.         register char *p;
  2170.         int flags;
  2171.  
  2172.         nextwin = curwin;
  2173.         if (prget("!", prbuf))
  2174.             break;
  2175.         p = prbuf;
  2176.         flags = CWAIT;
  2177.         if (*p == '\0') {
  2178.             strcpy(prbuf, SHELL);
  2179.             flags = 0;
  2180.         }
  2181.         while (*p) p++;
  2182.         while (p > prbuf && p[-1] == ' ')
  2183.             p--;
  2184.         if (*--p == '&') {
  2185.             *p = '\0';
  2186.             flags = BKGRND;
  2187.             strcpy(bfr, prbuf);
  2188.         } else if (*p == '|') {
  2189.             *p = '\0';
  2190.             sprintf(bfr, "(%s)2>&1|mail '%s'", prbuf, username);
  2191.             flags |= BKGRND;
  2192.         } else {
  2193.             nextwin = &emptywin;
  2194.             strcpy(bfr, prbuf);
  2195.         }
  2196.         shcmd(bfr, flags);
  2197.         }
  2198.         break;
  2199.  
  2200.     /* reply/followup */
  2201.     case 'r':
  2202.     case 'f':
  2203.     {
  2204.         static char *arg[3] = {bfr, bfr + FPATHLEN, NULL};
  2205.  
  2206. #ifdef REPLYPROG
  2207.         strcpy(bfr, REPLYPROG);
  2208. #else
  2209.         sprintf(bfr, "%s/postreply", LIB);
  2210. #endif
  2211.         sprintf(bfr + FPATHLEN, "-v%c%s", c, filename);
  2212.         prun(arg, 0);
  2213.         if (pstatus != 0) {
  2214.             msg("reply/followup %s", pstatus == 22<<8? "suppressed" : "command failed");
  2215.         }
  2216.         nextwin = &emptywin;
  2217.         break;
  2218.     }
  2219.  
  2220.     /* next newsgroup */
  2221.     case 'N':
  2222.         if (next_ng_command())
  2223.             quitflg = 1;
  2224.         break;
  2225.  
  2226.     /* display parent article */
  2227.     case 'p':
  2228.         parent_command() ;
  2229.         break;
  2230.  
  2231.     /* specific message ID. */
  2232.     case '<':
  2233.         if (prget("<", prbuf))
  2234.             break;
  2235.         goto_id(prbuf);
  2236.         break;
  2237.  
  2238.     /* erase - pretend we haven't seen this article. */
  2239.     case 'e':
  2240.         erased = 1;
  2241.         setnew(1);
  2242.         nextart(count, 0);
  2243.         break;
  2244.  
  2245.     case 'H':
  2246.         if (openart())
  2247.             break;
  2248.         nextwin = &hdrwin;
  2249.         break;
  2250.  
  2251.     case '#':
  2252.         msg("Article %d of %d (#%d of max %ld)",
  2253.             curind, numthisng, curart->i_artnum,
  2254.             pngsize);
  2255.         nextwin = curwin;
  2256.         break;
  2257.  
  2258.         /* error */
  2259.     case '?':
  2260.         nextwin = &helpwin;
  2261.         break;
  2262.  
  2263.     illegal:
  2264.     default:
  2265.         beep();
  2266.         nextwin = curwin;
  2267.         break;
  2268.     }
  2269.  
  2270.     return FALSE;
  2271. }
  2272.  
  2273. cancel_command()
  2274. {
  2275.     struct arthead *hptr = &h;
  2276.     char *ptr1;
  2277.     int i;
  2278.  
  2279.     if (openart())
  2280.         return;
  2281.     strcpy(bfr, hptr->h_path);
  2282.     ptr1 = index(bfr, ' ');
  2283.     if (ptr1)
  2284.         *ptr1 = 0;
  2285.     i = strcmp(username, bfr);
  2286.     if (i != 0) {
  2287.         if (isadmin())
  2288.             msg("Cancelling locally only");
  2289.         else {
  2290.             msg("Can't cancel what you didn't write.");
  2291.             return;
  2292.         }
  2293.     }
  2294.     if (!cancel(stderr, hptr, i) && hptr == &h) {
  2295.         nextart(1, 1);
  2296.     }
  2297. }
  2298.  
  2299.  
  2300.  
  2301. next_ng_command()
  2302. {
  2303.     struct ngentry *ngp;
  2304.  
  2305.     if (prget("group? ", bptr = prbuf))
  2306.         return FALSE;
  2307.     if (!*bptr || *bptr == '-') {
  2308.         return getnxtng(*bptr? BACKWARD : FORWARD);
  2309.     }
  2310.     while (isspace(*bptr))
  2311.         bptr++;
  2312.     if ((ngp = findgroup(bptr)) == NULL) {
  2313.         msg("No such group: %s.", bptr);
  2314.         return FALSE;
  2315.     }
  2316.     switchng(ngp);
  2317.     return FALSE;
  2318. }
  2319.  
  2320.  
  2321. /*
  2322.  * Find parent of article.
  2323.  */
  2324. parent_command() {
  2325.     struct artrec a;
  2326.     DPTR dp;
  2327.  
  2328.     readrec(curart->i_dptr, &a);
  2329.     if (a.a_parent == DNULL) {
  2330.         msg("no parent");
  2331.         nextwin = curwin;
  2332.         return;
  2333.     }
  2334.     readrec(dp = a.a_parent, &a);
  2335.     if (a.a_flags & A_NOFILE) {
  2336.         msg("parent not on system");
  2337.         nextwin = curwin;
  2338.         return;
  2339.     }
  2340.     spclgrp(dp, &a);
  2341. }
  2342.  
  2343.  
  2344. /*
  2345.  * display an article with a particular article or message ID.
  2346.  */
  2347. goto_id(ident)
  2348.     char *ident;
  2349.     {
  2350.     struct artrec a;
  2351.     DPTR dp;
  2352.     char *ptr1, *ptr2;
  2353.     char id[NAMELEN];
  2354.  
  2355.     bfr[0] = '<';
  2356.     strcpy(bfr+1, ident);
  2357.     if (index(bfr, '@') == NULL && index(bfr, '>') == NULL) {
  2358.         ptr1 = bfr;
  2359.         if (*ptr1 == '<')
  2360.             ptr1++;
  2361.         ptr2 = index(ptr1, '.');
  2362.         if (ptr2 != NULL) {
  2363.             *ptr2++ = '\0';
  2364.             sprintf(prbuf, "<%s@%s.UUCP>", ptr2, ptr1);
  2365.             strcpy(bfr, prbuf);
  2366.         }
  2367.     }
  2368.     if (index(bfr, '>') == NULL)
  2369.         strcat(bfr, ">");
  2370.     scopyn(bfr, id, NAMELEN);
  2371.     if ((dp = lookart(id, &a)) == DNULL) {
  2372.         msg("%s not found", ident);
  2373.         nextwin = curwin;
  2374.         return;
  2375.     }
  2376.     spclgrp(dp, &a);
  2377. }
  2378.  
  2379.  
  2380.  
  2381. /*
  2382.  * Unsubscribe to a discussion.
  2383.  */
  2384. ud_command() {
  2385.     struct artrec a;
  2386.     DPTR dp;
  2387.     register int i ;
  2388.  
  2389.     if (ndunsub >= MAXUNSUB) {
  2390.         msg("Unsubscribed to too many discussions");
  2391.         return;
  2392.     }
  2393.     dp = curart->i_dptr;
  2394.     readrec(dp, &a);
  2395.     while (a.a_parent != DNULL) {
  2396.         readrec(dp = a.a_parent, &a);
  2397.     }
  2398.     dunsub[ndunsub++] = dp;
  2399.     msg("unsubscribed to discussion %s", a.a_ident);
  2400.  
  2401.     /* Mark any articles in the discussion as read. */
  2402.     for (i = 0 ; i < numthisng ; i++) {
  2403.         if (thisng[i].i_basetime == a.a_subtime)
  2404.             if (alptr->al_type == SVNEWSGROUP)
  2405.                 clrunread(thisng[i].i_artnum) ;
  2406.     }
  2407.     nextart(1, 1) ;
  2408. }
  2409.  
  2410. /*
  2411.  * Execute a shell command.
  2412.  */
  2413.  
  2414. shcmd(cmd, flags)
  2415.     char *cmd;
  2416.     {
  2417.     char *arg[4];
  2418.  
  2419.     arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL;
  2420.     prun(arg, flags);
  2421. }
  2422.  
  2423.  
  2424. prun(args, flags)
  2425.     char **args;
  2426.     {
  2427.     int pid;
  2428.     int i;
  2429.     int (*savequit)();
  2430.     char *env[100], **envp;
  2431.     char a[BUFLEN + 2];
  2432.     extern char **environ;
  2433.  
  2434.     if (!(flags & BKGRND)) {
  2435.         botscreen();
  2436.         ttycooked();
  2437.     }
  2438.     while ((pid = fork()) == -1)
  2439.         sleep(1);        /* must not clear alarm */
  2440.     if (pid == 0) {
  2441.         for (i = 3 ; i < 20 ; i++)
  2442.             close(i);
  2443.         if (flags & BKGRND) {
  2444.             signal(SIGINT, SIG_IGN);
  2445.             signal(SIGQUIT, SIG_IGN);
  2446.             close(0), close(1), close(2);
  2447.             open("/dev/null", 2);
  2448.             dup(0), dup(0);
  2449.         }
  2450.         /* set $A */
  2451.         sprintf(a, "A=%s", filename);
  2452.         env[0] = a;
  2453.         for (envp = env + 1 ; *environ != NULL && envp < env + 98 ; environ++)
  2454.             if ((*environ)[0] != 'A' || (*environ)[1] != '=')
  2455.                 *envp++ = *environ;
  2456.         *envp = NULL;
  2457.  
  2458.         execve(args[0], args, env);
  2459.         fprintf(stderr, "%s: not found\n", args[0]);
  2460.         exit(20);
  2461.     }
  2462.     if (!(flags & BKGRND)) {
  2463.         savequit = signal(SIGQUIT, SIG_IGN);
  2464.         while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR));
  2465.         if (flags & CWAIT) {
  2466.             fprintf(stderr, "continue? ");
  2467.             while ((errno = 0, i = getchar()) != '\n'
  2468.                 && (i != EOF || errno == EINTR))
  2469. #ifdef old_version /* never worked under BSD */
  2470.                     if (intflag && i == EOF) {
  2471.                         fprintf(stderr, "\ncontinue? ");
  2472.                         intflag = 0;
  2473.                     }
  2474. #else
  2475.                     ;
  2476. #endif
  2477.         }
  2478.         signal(SIGQUIT, savequit);
  2479.         ttyraw();
  2480.         clearok(curscr, 1);
  2481.     }
  2482. }
  2483.  
  2484. #ifdef DIGPAGE
  2485.  
  2486.  
  2487. /*
  2488.  * Find end of current subarticle in digets.
  2489.  */
  2490.  
  2491. findend(l) {
  2492.     register i;
  2493.     register char *p;
  2494.  
  2495.     for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) {
  2496.         tfget(bfr, i);
  2497.         for (p = bfr ; *p == '-' ; p++);
  2498.         if (p > bfr + 24)
  2499.             return i + 1;
  2500.     }
  2501.     return 0;
  2502. }
  2503.  
  2504. #endif
  2505.  
  2506.  
  2507. char *
  2508. getmailname() {
  2509.     char mailname[FPATHLEN];
  2510.     register char *p;
  2511.  
  2512.     if( (p = getenv("MAIL")) != NULL)
  2513.         return p;
  2514. #if BSDREL > 7
  2515.     sprintf(mailname, "/usr/spool/mail/%s", username);
  2516. #else
  2517.     sprintf(mailname, "/usr/mail/%s", username);
  2518. #endif
  2519.     return savestr(mailname);
  2520. }
  2521.  
  2522.  
  2523.  
  2524. /*** stolen from rfuncs2.c and modified ***/
  2525.  
  2526. vsave(to, flags)
  2527. register char *to;
  2528. {
  2529.     register FILE *ufp;
  2530.     int    isprogram = 0;
  2531.     int    isnew = 1;
  2532.     long    saveoff;
  2533.     char    temp[20];
  2534.     char    *fname;
  2535.     char    prog[BUFLEN + 24];
  2536.  
  2537. #define hh h
  2538. #define hfp fp
  2539.     saveoff = ftell(fp);
  2540.     fname = to;
  2541.     if (*to == PIPECHAR) {
  2542.         isprogram++;
  2543.         if (strlen(to) > BUFLEN) {
  2544.             msg("Command name too long");
  2545.             goto out;
  2546.         }
  2547.         flags |= OVWRITE;
  2548.         strcpy(temp, "/tmp/vnXXXXXX");
  2549.         mktemp(temp);
  2550.         fname = temp;
  2551.         _amove(ROWS - 1, 0);
  2552.         vflush();
  2553.     }
  2554.     if ((flags & OVWRITE) == 0) {
  2555.         ufp = fopen(fname, "r");
  2556.         if (ufp != NULL) {
  2557.             fclose(ufp);
  2558.             isnew = 0;
  2559.         }
  2560.     }
  2561.     if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) {
  2562.         msg("Cannot open %s", fname);
  2563.         goto out;
  2564.     }
  2565.     /*
  2566.      * V7MAIL code is here to conform to V7 mail format.
  2567.      * If you need a different format to be able to
  2568.      * use your local mail command (such as four ^A's
  2569.      * on the end of articles) substitute it here.
  2570.      */
  2571.     if (flags & SVHEAD) {
  2572. #ifdef V7MAIL
  2573.         fprintf(ufp, "From %s %s",
  2574. #ifdef INTERNET
  2575.                 hh.h_from,
  2576. #else
  2577.                 hh.h_path,
  2578. #endif
  2579.                     ctime(&curart->i_subtime));
  2580. #endif
  2581. #ifdef V7MAIL
  2582.         fseek(fp, 0L, 0);
  2583.         while (fgets(bfr, LBUFLEN, fp) != NULL) {
  2584.             if (prefix(bfr, "From "))
  2585.                 putc('>', ufp);
  2586.             fputs(bfr, ufp);
  2587.         }
  2588.         putc('\n', ufp);    /* force blank line at end (ugh) */
  2589. #else
  2590.         fseek(fp, artbody, 0);
  2591.         while (fgets(bfr, LBUFLEN, fp) != NULL) {
  2592.             fputs(bfr, ufp);
  2593.         }
  2594. #endif
  2595.     } else {
  2596.         fseek(fp, artbody, 0);
  2597.         while (fgets(bfr, LBUFLEN, fp) != NULL) {
  2598.             fputs(bfr, ufp);
  2599.         }
  2600.     }
  2601.  
  2602.     fclose(ufp);
  2603.     if (isprogram) {
  2604.         sprintf(prog, "(%s)<%s", to + 1, fname);
  2605.         shcmd(prog, CWAIT);
  2606.         nextwin = &emptywin;
  2607.     } else {
  2608.         if ((flags & OVWRITE) == 0)
  2609.             msg("file: %s %s", to, isnew ? "created" : "appended");
  2610.         else
  2611.             msg("file: %s written", to);
  2612.     }
  2613.  
  2614. out:
  2615.     if (isprogram) {
  2616.         unlink(fname);
  2617.     }
  2618.     fseek(fp, saveoff, 0);
  2619. }
  2620.  
  2621.  
  2622.  
  2623. /*** routines originally in rfuncs.c ***/
  2624.  
  2625.  
  2626. xerror(fmt, a1, a2, a3, a4)
  2627. char    *fmt;
  2628. {
  2629.     int clean = errclean;
  2630.  
  2631.     errclean = 0;
  2632.     fflush(stdout);
  2633.     if (clean) {
  2634.         botscreen();
  2635.         ttycooked();
  2636.     }
  2637.     fprintf(stderr, "vnews: ");
  2638.     fprintf(stderr, fmt, a1, a2, a3, a4);
  2639.     fprintf(stderr, ".\n");
  2640.     if (clean)
  2641.         writeoutrc();
  2642.     xxit(1);
  2643. }
  2644.  
  2645.  
  2646.  
  2647. xxit(status)
  2648. int    status;
  2649. {
  2650.     exit(status);
  2651. }
  2652. !E!O!F!
  2653.  
  2654. echo Part 7 of 7 extracted.
  2655.  
  2656.  
  2657.